#include "date_utils.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 公历转儒略日
double JD(int y, int m, double d) {
    int n = 0, G = 0;
    if (y * 372 + m * 31 + (int)d >= 588829) G = 1; // 判断是否为格里高利历日 1582*372+10*31+15
    if (m <= 2) { m += 12; y--; }
    if (G) { n = (int)(y / 100); n = 2 - n + (int)(n / 4); } // 加百年闰
    return (int)(365.25 * (y + 4716)) + (int)(30.6001 * (m + 1)) + d + n - 1524.5;
}

// 儒略日数转公历
Date DD(double jd) {
    Date r;
    int D = (int)(jd + 0.5);
    double F = jd + 0.5 - D;
    int c;
    if (D >= 2299161) { c = (int)((D - 1867216.25) / 36524.25); D += 1 + c - (int)(c / 4); }
    D += 1524;
    r.Y = (int)((D - 122.1) / 365.25); // 年数
    D -= (int)(365.25 * r.Y);
    r.M = (int)(D / 30.601); // 月数
    D -= (int)(30.601 * r.M);
    r.D = D; // 日数
    if (r.M > 13) { r.M -= 13; r.Y -= 4715; }
    else { r.M -= 1; r.Y -= 4716; }
    // 日的小数转为时分秒
    F *= 24; r.h = (int)F; F -= r.h;
    F *= 60; r.m = (int)F; F -= r.m;
    r.s = F * 60;
    return r;
}

// 日期转为字符串
wchar_t* DD2str(Date r) {
    static wchar_t result[50];
    swprintf(result, sizeof(result) / sizeof(wchar_t), L"%04d-%02d-%02d %02d:%02d:%02d",
             r.Y, r.M, r.D, r.h, r.m, (int)(r.s + 0.5));
    return result;
}

// JD 转为字符串
wchar_t* JD2str(double jd) {
    Date r = DD(jd);
    return DD2str(r);
}

// 公历转儒略日
double toJD(Date *date) {
    return JD(date->Y, date->M, date->D + ((date->s / 60 + date->m) / 60 + date->h) / 24);
}

// 儒略日数转公历
void setFromJD(Date *date, double jd) {
    Date r = DD(jd);
    date->Y = r.Y;
    date->M = r.M;
    date->D = r.D;
    date->h = r.h;
    date->m = r.m;
    date->s = r.s;
}

// 提取 jd 中的时间（去除日期）
wchar_t* timeStr(double jd) {
    static wchar_t result[20];
    jd += 0.5;
    jd = jd - (int)jd;
    int s = (int)(jd * 86400 + 0.5);
    int h = s / 3600; s -= h * 3600;
    int m = s / 60;   s -= m * 60;
    swprintf(result, sizeof(result) / sizeof(wchar_t), L"%02d:%02d:%02d", h, m, s);
    return result;
}

// 星期计算
int getWeek(double jd) {
    return (int)(jd + 1.5 + 7000000) % 7;
}

// 求 y 年 m 月的第 n 个星期 w 的儒略日数
double nnweek(int y, int m, int n, int w) {
    double jd = JD(y, m, 1.5); // 月首儒略日
    int w0 = (int)(jd + 1 + 7000000) % 7; // 月首的星期
    double r = jd - w0 + 7 * n + w; // jd-w0+7*n 是和 n 个星期 0，起算下本月第一行的星期日（可能落在上一月）。加 w 后为第 n 个星期 w
    if (w >= w0) r -= 7; // 第 1 个星期 w 可能落在上个月，造成多算 1 周，所以考虑减 1 周
    if (n == 5) {
        m++; if (m > 12) { m = 1; y++; } // 下个月
        if (r >= JD(y, m, 1.5)) r -= 7; // r 跑到下个月则减 1 周
    }
    return r;
}